﻿namespace FSharpChess
module Move_check =
    let CanCastle(kingsq,ks,psn) =
        let pc = psn.Pcs.[kingsq]
        let pccol = pc.pcIsWhite
        let pcsqord = pc.SquareOrd
        let pr = Attack.getPlayer(pccol)
        let oth = Attack.OtherPlayer(pr)
        // King hasnt moved
        let testkm() = 
            if pccol then
                if ks then not psn.canK else not psn.canQ
            else 
                if ks then not psn.cank else not psn.canq
        // All squares between King and Rook are unoccupied
        let testemp() = 
            if ks then
                (psn.Sqs.[pcsqord + 1] <> -1 || psn.Sqs.[pcsqord + 2] <> -1) 
            else
                (psn.Sqs.[pcsqord - 1] <> -1 || psn.Sqs.[pcsqord - 2] <> -1 || psn.Sqs.[pcsqord - 3] <> -1)  
        // The king does not move over a square that is attacked by an enemy piece during the castling move
        let testattck() = 
            if ks then
                Attack.CanBeMovedToBy(pcsqord+1,oth,psn,true) || Attack.CanBeMovedToBy(pcsqord+2,oth,psn,true) 
            else
                Attack.CanBeMovedToBy(pcsqord-1,oth,psn,true) || Attack.CanBeMovedToBy(pcsqord-2,oth,psn,true)|| Attack.CanBeMovedToBy(pcsqord-3,oth,psn,true)
        // King is not in check or above
        if testkm() || testemp() || testattck() || Attack.is_in_check(pr,psn,false) then false else true
    let CanCastleK(psn) = CanCastle(4,true,psn)
    let CanCastleQ(psn) = CanCastle(4,false,psn)
    let CanCastlek(psn) = CanCastle(20,true,psn)
    let CanCastleq(psn) = CanCastle(20,false,psn)
    let move_is_check mv psn = 
        // slow test for complex moves
        if mv.mvName <> Standard then
            Move_do.DoMoveOK(mv,psn)|>ignore
            let check = Attack.is_in_check(psn.PlayerToPlay,psn,false)
            Move_do.Undo(mv,psn)
            check
        else
            let pc = psn.Pcs.[mv.mvPiece]
            //get king details
            let kingisW = not psn.PlayerToPlay.prIsWhite
            let kingid = if kingisW then 4 else 20
            let kingpc = psn.Pcs.[kingid]
            let kingsq = kingpc.SquareOrd
            //move based results
            let rec getMincheck ml =
                match ml with
                    |[] -> false 
                    | ml ->  
                        let cord = ml.Head 
                        if cord = kingsq then
                            true
                        else
                            getMincheck ml.Tail
            //get rays based results
            let rec getRincheck r rl =
                match r with
                    |[] -> if rl=[] then false else getRincheck rl.Head rl.Tail
                    | r ->  
                        let cord = r.Head 
                        if cord = kingsq then
                            true
                        elif psn.Sqs.[cord] = -1 then
                            getRincheck r.Tail rl 
                        else
                            if rl=[] then false else getRincheck rl.Head rl.Tail
            let direct_check() =
                let Bcheck () =
                        let diff = mv.To - kingsq
                        if (kingsq - mv.To)%7=0 then
                            let inc = if diff>0 then 7 else -7
                            Attack.line_is_empty psn kingsq mv.To inc
                        elif (kingsq - mv.To)%9=0 then
                            let inc = if diff>0 then 9 else -9
                            Attack.line_is_empty psn kingsq mv.To inc
                        else
                            false
                let Rcheck () =
                        let diff = mv.To - kingsq
                        if Ref.ri.[kingsq]=Ref.ri.[mv.To] then
                            let inc = if diff>0 then 1 else -1
                            Attack.line_is_empty psn kingsq mv.To inc
                        elif Ref.ri.[kingsq]=Ref.ri.[mv.To] then
                            let inc = if diff>0 then 8 else -8
                            Attack.line_is_empty psn kingsq mv.To inc
                        else
                            false
                match pc.Name with
                    |enmName.King -> false 
                    |enmName.Pawn -> getMincheck (if kingisW then Ref.attsPB.[mv.To] else Ref.attsPW.[mv.To])
                    |enmName.Knight -> getMincheck Ref.movsN.[mv.To]
                    |enmName.Bishop -> Bcheck()
                    |enmName.Rook -> Rcheck()
                    |enmName.Queen -> Bcheck()||Rcheck()
                    |_ -> raise InvalidName
            let indirect_check() =
                let diff = mv.From - kingsq
                //check whether on ray 
                if Ref.ri.[kingsq]=Ref.ri.[mv.From] then
                    let inc = if diff>0 then 1 else -1
                    if Ref.fi.[mv.From]=0||Ref.fi.[mv.From]=7 then
                        false
                    else
                        if Attack.line_is_empty psn kingsq mv.From inc then
                            let rec containsR sq =
                                if psn.Sqs.[sq] = -1 then
                                    if Ref.fi.[sq]=0||Ref.fi.[sq]=7 then false else containsR (sq+inc)
                                elif psn.Pcs.[psn.Sqs.[sq]].pcIsWhite <> kingisW && (psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Rook || psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Queen ) then
                                    true
                                else
                                    false
                            containsR (mv.From+inc)
                        else
                            false
                elif Ref.fi.[kingsq]=Ref.fi.[mv.From] then
                    let inc = if diff>0 then 8 else -8
                    if Ref.ri.[mv.From]=0||Ref.ri.[mv.From]=7 then
                        false
                    else
                        if Attack.line_is_empty psn kingsq mv.From inc then
                            let rec containsR sq =
                                if psn.Sqs.[sq] = -1 then
                                    if Ref.ri.[sq]=0||Ref.ri.[sq]=7 then false else containsR (sq+inc)
                                elif psn.Pcs.[psn.Sqs.[sq]].pcIsWhite <> kingisW && (psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Rook || psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Queen ) then
                                    true
                                else
                                    false
                            containsR (mv.From+inc)
                        else
                            false
                elif (kingsq - mv.From)%7=0 then
                    let inc = if diff>0 then 7 else -7
                    if Ref.ri.[mv.From]=0||Ref.ri.[mv.From]=7||Ref.fi.[mv.From]=0||Ref.fi.[mv.From]=7 then
                        false
                    else
                        if Attack.line_is_empty psn kingsq mv.From inc then
                            let rec containsB sq =
                                if psn.Sqs.[sq] = -1 then
                                    if Ref.ri.[sq]=0||Ref.ri.[sq]=7||Ref.fi.[mv.From]=0||Ref.fi.[mv.From]=7 then false else containsB (sq+inc)
                                elif psn.Pcs.[psn.Sqs.[sq]].pcIsWhite <> kingisW && (psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Bishop || psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Queen ) then
                                    true
                                else
                                    false
                            containsB (mv.From+inc)
                        else
                            false
                elif (kingsq - mv.From)%9=0 then
                    let inc = if diff>0 then 9 else -9
                    if Ref.ri.[mv.From]=0||Ref.ri.[mv.From]=7||Ref.fi.[mv.From]=0||Ref.fi.[mv.From]=7 then
                        false
                    else
                        if Attack.line_is_empty psn kingsq mv.From inc then
                            let rec containsB sq =
                                if psn.Sqs.[sq] = -1 then
                                    if Ref.ri.[sq]=0||Ref.ri.[sq]=7||Ref.fi.[mv.From]=0||Ref.fi.[mv.From]=7 then false else containsB (sq+inc)
                                elif psn.Pcs.[psn.Sqs.[sq]].pcIsWhite <> kingisW && (psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Bishop || psn.Pcs.[psn.Sqs.[sq]].Name=enmName.Queen ) then
                                    true
                                else
                                    false
                            containsB (mv.From+inc)
                        else
                            false
                else
                    false
            
            direct_check()||indirect_check()

